/**
  ETFRoot project, Anyang Normal University && IMP-CAS
  \class ETFVMEUnpacker
  \brief unpacker for raw data collected by VME daq
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-09-27
  \date 2021-10-01 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date        <th>Author      <th>Description                    </tr>
  <tr>  <td>2021-09-27   <td>Asia Sun    <td>file created                   </tr>
  <tr>  <td>2021-09-27   <td>Asia Sun    <td>completing                     </tr>
  </table>

  \copyright Copyright (c) 2021 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <TClonesArray.h>
#include "FairRootManager.h"

#include "ETFVMEUnpacker.h"
#include "ETFVMEEvent.h"
#include "ETFMsg.h"

using std::cout;
using std::endl;
using std::flush;

#define DEBUG_VME

ClassImp(ETFVMEUnpacker);

ETFVMEUnpacker::ETFVMEUnpacker() : ETFUnpacker(1), fEvent(new ETFVMEEvent())
  ,fData{0}, fPos(0), fIndex0(-1), fBlockCnt(0)
  ,fDisplayComment(true), fDebug(false){}

ETFVMEUnpacker::~ETFVMEUnpacker(){
  if(fEvent){ delete fEvent; fEvent = nullptr; }
} // end the dtor

bool ETFVMEUnpacker::Init(){
  Register();
  return true;
} // end member function Init

void ETFVMEUnpacker::Register(){
  FairRootManager *man = FairRootManager::Instance();
  if(man){
    // branchname, foldername, array address, persistent or transient
    man->Register("VMEEvent", "VME", fEvent, true);
    man->Register("tchvme", "VME", fEvent->fHPTDCChannels, true);
    man->Register("achvme", "VME", fEvent->fAmplitudeChannels, true);
  } // end if
} // end member function Register

void ETFVMEUnpacker::Reset(){
  fEvent->Reset();
} // end member function Reset



//----------------------------VME---------------------------------------------//
//---------------------------UNPACK-------------------------------------------//
//----------------------------CODE--------------------------------------------//


void ETFVMEUnpacker::ReadComment(FILE *fp){
  /// \TODO may need check if this is the 1st block of the file
  char comment[sizeof(fData)];
  if(fread(comment, sizeof(int), kBLKSIZE, fp) <= 0)
    ETFMsg::Error("ETFVMEUnpacker", "ReadComment: Reading file failure");
  if(!fDisplayComment) return;

  cout << "\n\nVME data file comment:\n\n\033[36;1m";
  for(char c : comment) if(c) cout << c;
  cout << "\n\033[0m";
} // end member function ReadComment

/// get one event from offline data file to memory (fData)
int ETFVMEUnpacker::GetOneEvent(FILE *fp){
  fPos += fEvent->fEventHead.event_length; // update fPos to next event
  // read the block
  if(fBlockHead.finished()){ // the current block has been read
    if(fread(fData, sizeof(int), kBLKSIZE, fp) <= 0) return -1;
    fBlockHead.setData(fData); // assign the block header
    fBlockCnt++;
    fPos = 3; // the first 3 words has been read
    if(fDebug) fBlockHead.print();
  } // end if

  vme_event_head &ev_h = fEvent->fEventHead;
  // [0-1-2]: event_len-event_index-vmeReadCount
  ev_h.event_length = fData[fPos];
  ev_h.index = fData[fPos+1];
  if(-1 == fIndex0) fIndex0 = fData[fPos+1];
  if(fIndex0) ev_h.index -= fIndex0;
  ev_h.vmeReadCount = fData[fPos+2];
  if(fDebug) ev_h.print();
  fBlockHead.current_event++;

  return fCurrentEventIndex = ev_h.index;
} // end member function GetOneEvent

/// get one event from online data stream to memory
/// \TODO on the premise that `stream` updates to point to each new block
/// upon calling this method, or one should explicitly update it here
int ETFVMEUnpacker::GetOneEvent(int *stream){
  fPos += fEvent->fEventHead.event_length; // update fPos to next event
  // read the block
  if(fBlockHead.finished()){ // the current block has been read
    memcpy(fData, stream, sizeof(int)*kBLKSIZE);
    fBlockHead.setData(fData); // assign the block header
    fBlockCnt++;
    fPos = 3; // the first 3 words has been read
    if(fDebug) fBlockHead.print();
  } // end if

  vme_event_head &ev_h = fEvent->fEventHead;
  // [0-1-2]: event_len-event_index-vmeReadCount
  ev_h.event_length = fData[fPos];
  ev_h.index = fData[fPos+1];
  if(-1 == fIndex0) fIndex0 = fData[fPos+1];
  if(fIndex0) ev_h.index -= fIndex0;
  ev_h.vmeReadCount = fData[fPos+2];
  if(fDebug) ev_h.print();
  fBlockHead.current_event++;

  return fCurrentEventIndex = ev_h.index;
} // end member function GetOneEvent


/// do the rest of the unpacking after calling GetOneBlock(). Since 1 block may
/// contain several events, Unpack() should be looped for all the events in the
/// block. This should be steered in the corresponding ETFOfflineSource::ReadEvent().
/// \note that the method is called by ETFOfflineSource::ReadEvent(), where Unpack()
/// would be bypassed for failure of GetOneBlock()
bool ETFVMEUnpacker::Unpack(){
  if(!fData){
    ETFMsg::Error("ETFVMEUnpacker", "Unpack: fData is nullptr");
    return false;
  } // end if(!fData)

  // decode a VME event
  UserDecode();

  if(fDebug) fEvent->Print();
  fEventCnt++;

  return true;
} // end member function Unpack

void ETFVMEUnpacker::PrintSummary(){
  cout << "\n>>>>>>>>>>>>>> VME DATA Total BLOCK Count <<<<<<<<<<<<<<<<<<<\n" << endl;
	cout << "                         \033[33;1m" << fBlockCnt << "\033[0m\n" << endl;
	cout << ">>>>>>>>>>>>>>>>> VME DATA Total EVENT Count <<<<<<<<<<<<<<<<<<<<\n" << endl;
  cout << "                            \033[1m" << fEventCnt << "\033[0m\n" << endl;
  cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" << endl;

  cout << "\033[45;1m .... .... ... RAW VME DATA DECODEING HAS BEEN COMPLETED .... .... ... \033[0m\n\n\n" << endl;
} // end member function PrintSummary
